#include <cstdlib>
#include <iostream>
#include <vector>
#include "structs.h"
#include <fstream>

//#define DEBUG 1

Stack2::Stack2(int x) {
    this->top = new Move[x];
    this->size = -1;
    this->maxsize = x;
}

void printM(Move* m) {
    cout << "           Disc : " << m->Disc << " from : " << m->from << " to : " << m->to << endl;
}

void printM(Move* m,ofstream& log) {
    log << "           Disc : " << m->Disc << " from : " << m->from << " to : " << m->to << " turn : "<< m->turn<<endl;
}

void Stack2::Push(Move* move) {
    if (size == maxsize - 1) {
        //        cout << "Doubling stack" <<endl;
        Move* newTop = new Move[2 * maxsize];
        for (int i = 0; i <= size; i++) {
            newTop[i] = top[i];
        }
        delete[] top;
        top = newTop;
        maxsize = 2 * maxsize;
    }

    this->size++;
    this->top[size] = *move;


}

Move* Stack2::Top() {
    //    cout << "popping from place " << this->size-1 << endl;
    if (size>-1)
        return &top[size];
    else {
        Move move;
        move.turn = 0;
        return NULL;
    }
    //    return top;
}

void Stack2::Pop() {

    //    this->top = this->top - sizeof(Move);
    //    this->top--;
    //    cout << "stack resize to " << this->size-1 << endl;
    //    this->top = this->top - sizeof(Move*);
    size--;
}

bool Stack2::isEmpty() {
    if (this->size == -1) return true;
    else return false;
}

int Stack2::getSize() {
    return this->size;
}

void Stack2::printStack(ofstream& log) {
    for (int i = 0; i <= size; i++) {
        //        for (int j=0;j<top[i].size();j++)
        //        printM(&this->top[i].at(j));
        printM(&this->top[i],log);
    }
}

void Stack2::split(vector<Move>* out,ofstream& log) {
    int turn=0;
    vector<Move*> moves,tmp;
    Move* newStack;
#ifdef DEBUG    
    log << "SPLITTING STACK " << endl;
    this->printStack(log);
    log << "END OF STACK" <<endl;
#endif    
            
            
    for(int i=0;i<=this->size;i++) {  
        moves.push_back(&top[i]);
        if (i==0) turn = top[i].turn;
        if (top[i].turn!=turn || i==this->size) {
            if (i==this->size) {
            for (int j=0;j<=moves.size()/2;j++) {
                out->push_back(*moves.at(j));
            }
            
            for (int j=(moves.size()/2)+1;j<moves.size();j++) {
                tmp.push_back(moves.at(j));
                
            }
            } 
            else {
                for (int j=0;j<moves.size()/2;j++) {
                out->push_back(*moves.at(j));
            }
            
            for (int j=moves.size()/2;j<moves.size();j++) {
                tmp.push_back(moves.at(j));
                
            }
            }
            
            
            moves.clear();
            turn = top[i].turn;
        }
//        moves.push_back(&top[i]);
    }
    this->maxsize = (tmp.size()*3)/2;
    if (this->maxsize==0) this->maxsize = 10;
    newStack = new Move[this->maxsize];
    this->size = tmp.size()-1;
    for(int i=0;i<=this->size;i++) {
        newStack[i] = *tmp.at(i);
    }
    free(this->top);
    this->top = newStack;
#ifdef DEBUG    
    log << "SPLITTED PART OF STACK " << endl;
            for (int j=0;j<out->size();j++) {
                printM(&out->at(j),log);
            }
    
    log << "STACK MAXSIZE = " << this->maxsize << " SIZE = " << this->size << endl;
    log << "STACK AFTER SPLITTING" << endl;
    this->printStack(log);
    log << "END OF STACK" <<endl;
#endif    
}

void Stack2::del() {
    this->size = -1;
}

void Comm::send(vector<Move>* in,int dest,ofstream& log) {
    int size = 4*(in->size());
    int* data = (int*)malloc(size*sizeof(int));
#ifdef DEBUG    
    log << "SENDING DATA TO "<< dest<< " SIZE "<<size<<endl;
#endif    
    for(int i=0;i<in->size();i++) {
#ifdef DEBUG        
        printM(&in->at(i),log);
#endif        
        data[i*4]=in->at(i).Disc;
        data[(i*4)+1]=in->at(i).from;
        data[(i*4)+2]=in->at(i).to;
        data[(i*4)+3]=in->at(i).turn;
    }
    
    
    
    MPI_Send(&size, 1, MPI_INT, dest, MSG_DATA_HEADER, MPI_COMM_WORLD);
    MPI_Send(data, size, MPI_INT, dest, MSG_DATA, MPI_COMM_WORLD);
#ifdef DEBUG    
    log << "SENDING COMPLETE" << endl;
#endif    

}

void Comm::recieve(vector<Move>* out,int from,ofstream& log) {
    
    int size;
    MPI_Status status;
    Move* move;
#ifdef DEBUG    
    log << "RECIEVING DATA" << endl;
#endif    
    MPI_Recv(&size, 1, MPI_INT, from, MSG_DATA_HEADER, MPI_COMM_WORLD, &status);
    int* data = (int*)malloc(size*sizeof(int));
    MPI_Recv(data, size, MPI_INT, from, MSG_DATA, MPI_COMM_WORLD, &status);
#ifdef DEBUG    
    log << "DATA RECIEVED FROM " << status.MPI_SOURCE << " SIZE "<<size<<endl;
#endif    
    
    for(int i=0;i<size/4;i++) {
        move = new Move();
        move->Disc = data[i*4];
        move->from=data[(i*4)+1];
        move->to=data[(i*4)+2];
        move->turn=data[(i*4)+3];
        out->push_back(*move);
#ifdef DEBUG        
        printM(move,log);
#endif        
    }
}

